package ba02_AfterReturning;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

/**
 * @author HLF
 * @version V1.0
 * @package ba01
 * @date 2021/10/17 10:19
 * @Aspect 是aspectj框架的注解，表示当前类是切面类
 * 切面类：用来给业务方法增加功能的类，在这个类中有增加切面功能的代码
 * 位置：在切面类定义的上面
 */
@Aspect
public class MyAspect {

    /**
     * 后置通知定义方法，方法是实现切面功能的。
     * 方法的定义要求：
     * 1.公共方法 public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法是有参数的，推荐是Object，参数名自定义
     * 5.方法参数可以使用JoinPoint，但必须在参数的第一个位置
     */
    /**
     * @AfterReturning 后置通知
     * 属性：1.value 切入点表达式
     * 2.returning 自定义的变量，表示目标方法的返回值的。
     * 自定义变量名必须和通知方法的形参名一样
     * 位置：在方法定义的上面
     * 特点：
     * 1.在目标方法之后执行
     * 2.能够获取到目标方法的返回值，可以根据这个返回值做不同的处理功能
     * Object res = doOther();
     * 3.可以修改这个返回值
     * <p>
     * <p>
     * 后置通知的执行
     * Object res = doOther();
     * myAfterReturning(res);
     * <p>
     * 如果res类型是基本数据类型或String类型，res不会发生改变
     * 如果res类型是其他引用数据类型，res发生改变
     * <p>
     * 来自CSDN:
     * String(基本数据类型)在代理类中改变了值，但最终目标类的返回值依旧是"abc"而不是"1234"。
     * Student(引用类型)在代理类中改变了值，最终目标类的返回值确实从"玫瑰"变成了"幽灵"。
     */

    @AfterReturning(value = "execution(* ba02_AfterReturning.SomeServiceImpl.doOther(..))",
            returning = "res")
    public void myAfterReturning(JoinPoint joinPoint, Object res) {
        //Object res 是目标方法执行后的返回值，根据返回值做你的切面的功能处理
        System.out.println("后置通知，方法的定义：" + joinPoint.getSignature());
        System.out.println("后置通知，在目标方法执行之后执行的，获取目标方法的返回值是：" + res);
        if (res.equals("abcd")) {
            //做一些功能
        } else {
            //做其他功能
        }

        //修改目标方法的返回值， 看一下是否会影响 最后的方法调用结果
        if (res != null) {
            res = "Hello Aspectj";//这里只是增强功能，并不会影响目标类的目标方法中的返回值
        }
    }


    //作业
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther2(..))",
            returning = "res")
    public void myAfterReturning2(JoinPoint joinPoint, Student res) {//JoinPoint对象参数必须在第一个位置
        // Object res:是目标方法执行后的返回值，根据返回值做你的切面的功能处理
        System.out.println("后置通知：在目标方法之后执行的，获取的返回值是：" + res);

        //修改目标方法的返回值， 看一下是否会影响 最后的方法调用结果
        //如果修改了res的内容，属性值等，是不是会影响最后的调用结果呢
        if (res != null) {
            Object[] args = joinPoint.getArgs();
            res.setName((String) args[0]);
            res.setAge((Integer) args[1]);
        }
        /**
         * 事实证明，java基本数据类型对象和String对象不会改变
         * 其他引用数据类型对象进行址传递，发生改变
         */
    }
}
